package com.xmut.eduservice.userCF;

import java.util.*;
import java.util.Map.Entry;

/**
 * 基于用户的协同过滤推荐算法实现
 * A a b d
 * B a c
 * C b e
 * D c d e
 *
 * @author Administrator
 */
public class UserCF {
    int[][] sparseMatrix;//建立用户稀疏矩阵，用于用户相似度计算【相似度矩阵】
    Map<String, Integer> userItemLength = new HashMap<>();//存储每一个用户对应的不同物品总数  eg: A 3
    Map<String, Set<String>> itemUserCollection = new HashMap<>();//建立物品到用户的倒排表 eg: a A B
    Set<String> items = new HashSet<>();//辅助存储物品集合
    Map<String, Integer> userID = new HashMap<>();//辅助存储每一个用户的用户ID映射
    Map<Integer, String> idUser = new HashMap<>();//辅助存储每一个ID对应的用户映射

    // 获取用户之间的相关度
    public List<UserCFOutputUserSimilarityVo> getCorrelationBetweenUsers(String recommendUser) {//<eg:A>
        List<UserCFOutputUserSimilarityVo> userCFOutputUserSimilarityVoList = new ArrayList<>();
        //计算用户之间的相似度【余弦相似性】
        int recommendUserId = userID.get(recommendUser);
        for (int i = 0; i < sparseMatrix.length; i++) {
            if (i != recommendUserId) {
                UserCFOutputUserSimilarityVo userCFOutputUserSimilarityVo = new UserCFOutputUserSimilarityVo();
                userCFOutputUserSimilarityVo.setUserIdOne(idUser.get(recommendUserId));
                userCFOutputUserSimilarityVo.setUserIdTwo(idUser.get(i));
                userCFOutputUserSimilarityVo.setSimilarity(sparseMatrix[recommendUserId][i] / Math.sqrt(userItemLength.get(idUser.get(recommendUserId)) * userItemLength.get(idUser.get(i))));
                userCFOutputUserSimilarityVoList.add(userCFOutputUserSimilarityVo);
                System.out.println(userCFOutputUserSimilarityVo.getUserIdOne() + "--" + userCFOutputUserSimilarityVo.getUserIdTwo() + "相似度:" + userCFOutputUserSimilarityVo.getSimilarity());
            }
//            if(i != recommendUserId){
//                System.out.println(idUser.get(recommendUserId)+"--"+idUser.get(i)+"相似度:"+sparseMatrix[recommendUserId][i]/Math.sqrt(userItemLength.get(idUser.get(recommendUserId))*userItemLength.get(idUser.get(i))));
//            }
        }
        return userCFOutputUserSimilarityVoList;
    }

    //计算指定用户recommendUser的课程推荐度
    public List<UserCFOutputRecommendedDegreeVo> getCourseRecommendation(String recommendUser) {
        List<UserCFOutputRecommendedDegreeVo> userCFOutputRecommendedDegreeVoList = new ArrayList<>();
        for (String item : items) {//遍历每一件物品
            UserCFOutputRecommendedDegreeVo userCFOutputRecommendedDegreeVo = new UserCFOutputRecommendedDegreeVo();
            Set<String> users = itemUserCollection.get(item);//得到购买当前物品的所有用户集合
            if (!users.contains(recommendUser)) {//如果被推荐用户没有购买当前物品，则进行推荐度计算
                double itemRecommendDegree = 0.0;
                for (String user : users) {
                    itemRecommendDegree += sparseMatrix[userID.get(recommendUser)][userID.get(user)] / Math.sqrt(userItemLength.get(recommendUser) * userItemLength.get(user));//推荐度计算
                }
                userCFOutputRecommendedDegreeVo.setCourseId(item);
                userCFOutputRecommendedDegreeVo.setUserId(recommendUser);
                userCFOutputRecommendedDegreeVo.setRecommendedDegree(itemRecommendDegree);
                System.out.println("The item " + item + " for " + recommendUser + "'s recommended degree:" + itemRecommendDegree);
                userCFOutputRecommendedDegreeVoList.add(userCFOutputRecommendedDegreeVo);
            }
        }
        return userCFOutputRecommendedDegreeVoList;
    }

    // 更新推荐算法
    public void refreshUserCF(List<UserCFInputVo> userCFInputVoList) {
        //输入用户总量
        int N = userCFInputVoList.size();
        this.sparseMatrix = new int[N][N];//建立用户稀疏矩阵，用于用户相似度计算【相似度矩阵】
        this.userItemLength = new HashMap<>();//存储每一个用户对应的不同物品总数  eg: A 3
        this.itemUserCollection = new HashMap<>();//建立物品到用户的倒排表 eg: a A B
        this.items = new HashSet<>();//辅助存储物品集合
        this.userID = new HashMap<>();//辅助存储每一个用户的用户ID映射
        this.idUser = new HashMap<>();//辅助存储每一个ID对应的用户映射

        for (int i = 0; i < N; i++) {//依次处理N个用户 输入数据  以空格间隔
            int length = userCFInputVoList.get(i).getCourseIdList().size();
            String user = userCFInputVoList.get(i).getUserId();

            userItemLength.put(user, length);//eg: A 3
            userID.put(user, i);//用户ID与稀疏矩阵建立对应关系
            idUser.put(i, user);
            //建立物品--用户倒排表
            for (int j = 0; j < length; j++) {
                String item = userCFInputVoList.get(i).getCourseIdList().get(j);
                if (items.contains(item)) {//如果已经包含对应的物品--用户映射，直接添加对应的用户
                    itemUserCollection.get(item).add(user);
                } else {//否则创建对应物品--用户集合映射
                    items.add(item);
                    itemUserCollection.put(item, new HashSet<String>());//创建物品--用户倒排关系
                    itemUserCollection.get(item).add(user);
                }
            }
        }
        System.out.println(itemUserCollection.toString());

        //计算相似度矩阵【稀疏】
        Set<Entry<String, Set<String>>> entrySet = itemUserCollection.entrySet();
        Iterator<Entry<String, Set<String>>> iterator = entrySet.iterator();
        while (iterator.hasNext()) {
            Set<String> commonUsers = iterator.next().getValue();
            for (String user_u : commonUsers) {
                for (String user_v : commonUsers) {
                    if (user_u.equals(user_v)) {
                        continue;
                    }
                    sparseMatrix[userID.get(user_u)][userID.get(user_v)] += 1;//计算用户u与用户v都有正反馈的物品总数
                }
            }
        }
    }

    // 初始化推荐算法
    public UserCF(List<UserCFInputVo> userCFInputVoList) {
        this.refreshUserCF(userCFInputVoList);
    }
}